package bibbletex.models;

import java.util.Comparator;
import java.util.UUID;

import bibbletex.controllers.ReferenceController;

/**
 * Model for a reference
 * 
 * TODO: create subclasses for the different types instead of having
 * ReferenceType?
 * 
 * @author Diego Barrios Romero
 * 
 */
public class Reference {

	/**
	 * Type of reference
	 * 
	 * @author Diego Barrios Romero
	 * 
	 */
	public enum ReferenceType {
		ARTICLE, BOOK, BOOKLET, CONFERENCE, INBOOK, INCOLLECTION, INPROCEEDINGS, MANUAL, MASTERSTHESIS, MISC, PHDTHESIS, PROCEEDINGS, TECHREPORTS, UNPUBLISHED;
	};

	/**
	 * Unique identifier. This will also work as the primary key in the
	 * database.
	 */
	UUID id;
	String label, title, authors, booktitle, editor, 
		publisher, organization, school, journal, year, volume, number,
		pages, month, note, series, address, edition, isbn, howpublished;
	boolean is_suggestion;
	float rating;
	int ratingcounter;

	ReferenceType type;

	/**
	 * Constructor for a dummy Reference
	 */
	public Reference() {
		id = UUID.randomUUID();
		rating = 0;
		ratingcounter = 0;
	}
	
	/**
	 * Constructor with ID, to be used only by load
	 * 
	 * @param id
	 *            UUID loaded
	 */
	public Reference(UUID id) {
		this.id = id;
		rating = 0;
		ratingcounter = 0;
	}

	/**
	 * Copy constructor
	 * 
	 * @param r
	 *            Reference
	 */
	public Reference(Reference r) {
		id = r.getId();
		setRating(r.getRating());
		setRatingcounter(r.getRatingcounter());
		setTitle(r.getTitle());
		setType(r.getType());
		setAuthors(r.getAuthors());
		setLabel(r.getLabel());
		setBooktitle(r.getBooktitle());
		setEditor(r.getEditor());
		setPublisher(r.getPublisher());
		setOrganization(r.getOrganization());
		setSchool(r.getSchool());
		setJournal(r.getJournal());
		setYear(r.getYear());
		setVolume(r.getVolume());
		setNumber(r.getNumber());
		setPages(r.getPages());
		setMonth(r.getMonth());
		setNote(r.getNote());
		setSeries(r.getSeries());
		setAddress(r.getAddress());
		setEdition(r.getEdition());
		setIsbn(r.getIsbn());
		setHowpublished(r.getHowpublished());
	}

	/**
	 * Constructor to be used with form data
	 * 
	 * @param rr
	 *            request data
	 */
	public Reference(RequestReference rr) {
		id = UUID.randomUUID();
		
		setRating(0);
		setRatingcounter(0);
		setTitle(rr.getTitle());
		setType(rr.getType());
		setAuthors(rr.getAuthors());
		setLabel(rr.getLabel());
		setBooktitle(rr.getBooktitle());
		setEditor(rr.getEditor());
		setPublisher(rr.getPublisher());
		setOrganization(rr.getOrganization());
		setSchool(rr.getSchool());
		setJournal(rr.getJournal());
		setYear(rr.getYear());
		setVolume(rr.getVolume());
		setNumber(rr.getNumber());
		setPages(rr.getPages());
		setMonth(rr.getMonth());
		setNote(rr.getNote());
		setSeries(rr.getSeries());
		setAddress(rr.getAddress());
		setEdition(rr.getEdition());
		setIsbn(rr.getIsbn());
		setHowpublished(rr.getHowpublished());
	}

	/**
	 * Constructor to be used with form data
	 * 
	 * @param rr
	 *            request data
	 */
	public Reference(RequestReference rr, UUID uuid) {
		id = uuid;
		Reference r = ReferenceController.load(uuid.toString());
		if (r != null)
			setRating(r.getRating());
		else
			setRating(0);
		
		if (r != null)
			setRatingcounter(r.getRatingcounter());
		else
			setRatingcounter(0);
		
		setTitle(rr.getTitle());
		setType(rr.getType());
		setAuthors(rr.getAuthors());
		setLabel(rr.getLabel());
		setBooktitle(rr.getBooktitle());
		setEditor(rr.getEditor());
		setPublisher(rr.getPublisher());
		setOrganization(rr.getOrganization());
		setSchool(rr.getSchool());
		setJournal(rr.getJournal());
		setYear(rr.getYear());
		setVolume(rr.getVolume());
		setNumber(rr.getNumber());
		setPages(rr.getPages());
		setMonth(rr.getMonth());
		setNote(rr.getNote());
		setSeries(rr.getSeries());
		setAddress(rr.getAddress());
		setEdition(rr.getEdition());
		setIsbn(rr.getIsbn());
		setHowpublished(rr.getHowpublished());
	}
	
	public UUID getId() {
		return id;
	}

	public void setId(UUID uuid) {
		id = uuid;
	}
	
	public float getRating() {
		return rating;
	}

	public void setRating(float rating) {
		this.rating = rating;
	}
	
	public int getRatingcounter() {
		return ratingcounter;
	}

	public void setRatingcounter(int ratingcounter) {
		this.ratingcounter = ratingcounter;
	}
	
	public String getTitle() {
		return title;
	}

	public void setTitle(String title_) {
		if (title_ != null)
			title = new String(title_);
	}

	public String getAuthors() {
		return authors;
	}

	public void setAuthors(String authors_) {
		if (authors_ != null)
		authors = new String(authors_);
	}

	public ReferenceType getType() {
		return type;
	}

	public void setType(ReferenceType type_) {
		// WARNING: Reference!
		type = type_;
	}

	public void setType(String type_) {
		if (type_.equals("0") || type_.toUpperCase().equals("ARTICLE"))
			type = ReferenceType.ARTICLE;
		else if (type_.equals("1") || type_.toUpperCase().equals("BOOK"))
			type = ReferenceType.BOOK;
		else if (type_.equals("2") || type_.toUpperCase().equals("BOOKLET"))
			type = ReferenceType.BOOKLET;
		else if (type_.equals("3") || type_.toUpperCase().equals("CONFERENCE"))
			type = ReferenceType.CONFERENCE;
		else if (type_.equals("4") || type_.toUpperCase().equals("INBOOK"))
			type = ReferenceType.INBOOK;
		else if (type_.equals("5") || type_.toUpperCase().equals("INCOLLECTION"))
			type = ReferenceType.INCOLLECTION;
		else if (type_.equals("6") || type_.toUpperCase().equals("INPROCEEDINGS"))
			type = ReferenceType.INPROCEEDINGS;
		else if (type_.equals("7") || type_.toUpperCase().equals("MANUAL"))
			type = ReferenceType.MANUAL;
		else if (type_.equals("8") || type_.toUpperCase().equals("MASTERSTHESIS"))
			type = ReferenceType.MASTERSTHESIS;
		else if (type_.equals("9") || type_.toUpperCase().equals("MISC"))
			type = ReferenceType.MISC;
		else if (type_.equals("10") || type_.toUpperCase().equals("PHDTHESIS"))
			type = ReferenceType.PHDTHESIS;
		else if (type_.equals("11") || type_.toUpperCase().equals("PROCEEDINGS"))
			type = ReferenceType.PROCEEDINGS;
		else if (type_.equals("12") || type_.toUpperCase().equals("TECHREPORTS"))
			type = ReferenceType.TECHREPORTS;
		else if (type_.equals("13") || type_.toUpperCase().equals("UNPUBLISHED"))
			type = ReferenceType.UNPUBLISHED;
		else
			System.err.println("Reference: Unknown type: " + type_);
	}
	
	public boolean is_suggestion() {
		return is_suggestion;
	}

	public void setIs_suggestion(boolean is_suggestion) {
		this.is_suggestion = is_suggestion;
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		if (label != null)
		this.label = new String(label);
	}

	public String getBooktitle() {
		return booktitle;
	}

	public void setBooktitle(String booktitle) {
		if (booktitle != null)
		this.booktitle = new String(booktitle);
	}

	public String getEditor() {
		return editor;
	}

	public void setEditor(String editor) {
		if (editor != null)
		this.editor = new String(editor);
	}

	public String getPublisher() {
		return publisher;
	}

	public void setPublisher(String publisher) {
		if (publisher != null)
		this.publisher = new String(publisher);
	}

	public String getOrganization() {
		return organization;
	}

	public void setOrganization(String organization) {
		if (organization != null)
		this.organization = new String(organization);
	}

	public String getSchool() {
		return school;
	}

	public void setSchool(String school) {
		if (school != null)
		this.school = new String(school);
	}

	public String getJournal() {
		return journal;
	}

	public void setJournal(String journal) {
		if (journal != null)
		this.journal = new String(journal);
	}

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		if (year != null)
		this.year = new String(year);
	}

	public String getVolume() {
		return volume;
	}

	public void setVolume(String volume) {
		if (volume != null)
		this.volume = new String(volume);
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		if (number != null)
		this.number = new String(number);
	}

	public String getPages() {
		return pages;
	}

	public void setPages(String pages) {
		if (pages != null)
		this.pages = new String(pages);
	}

	public String getMonth() {
		return month;
	}

	public void setMonth(String month) {
		if (month != null)
		this.month = new String(month);
	}

	public String getNote() {
		return note;
	}

	public void setNote(String note) {
		if (note != null)
		this.note = new String(note);
	}

	public String getSeries() {
		return series;
	}

	public void setSeries(String series) {
		if (series != null)
		this.series = new String(series);
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		if (address != null)
		this.address = new String(address);
	}

	public String getEdition() {
		return edition;
	}

	public void setEdition(String edition) {
		if (edition != null)
		this.edition = new String(edition);
	}

	public String getIsbn() {
		return isbn;
	}

	public void setIsbn(String isbn) {
		if (isbn != null)
		this.isbn = new String(isbn);
	}

	public String getHowpublished() {
		return howpublished;
	}

	public void setHowpublished(String howpublished) {
		if (howpublished != null)
		this.howpublished = new String(howpublished);
	}

	/* HELPER METHODS */
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof Reference) {
			Reference other = (Reference) obj;
			// By definition, the UUID are uniques so we only need to
			// compare the ID
			return (id.equals(other.id));
		} else {
			return false;
		}
	}
	
	public boolean contains(String q){
		boolean temp = false;
		if(getTitle() != null){
			if(getTitle().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getAuthors() != null){
			if(getAuthors().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getType() != null){
			if(getType().toString().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getBooktitle() != null){
			if(getBooktitle().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getJournal() != null){
			if(getJournal().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getSchool() != null){
			if(getSchool().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getAddress() != null){
			if(getAddress().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getEdition() != null){
			if(getEdition().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getEditor() != null){
			if(getEditor().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getHowpublished() != null){
			if(getHowpublished().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getIsbn() != null){
			if(getIsbn().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getMonth() != null){
			if(getMonth().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getNote() != null){
			if(getNote().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getNumber() != null){
			if(getNumber().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getOrganization() != null){
			if(getOrganization().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getPages() != null){
			if(getPages().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getPublisher() != null){
			if(getPublisher().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getVolume() != null){
			if(getVolume().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		if(getYear() != null){
			if(getYear().toLowerCase().contains(q.toLowerCase())){
				temp = true;
			}
		}
		return temp;
	}
	
	/* COMPARATORS */
	public static class CompareByAuthorASC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r1.getAuthors().toLowerCase().compareTo(r2.getAuthors().toLowerCase()));
		}
	}
	
	public static class CompareByAuthorDESC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r2.getAuthors().toLowerCase().compareTo(r1.getAuthors().toLowerCase()));
		}
	}
	
	public static class CompareByTypeASC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r1.getType().toString().compareTo(r2.getType().toString()));
		}
	}
	
	public static class CompareByTypeDESC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r2.getType().toString().compareTo(r1.getType().toString()));
		}
	}
	
	public static class CompareByTitleASC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r1.getTitle().toLowerCase().compareTo(r2.getTitle().toLowerCase()));
		}
	}
	
	public static class CompareByTitleDESC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return (r2.getTitle().toLowerCase().compareTo(r1.getTitle().toLowerCase()));
		}
	}
	
	public static class CompareByRelevanceASC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return Float.compare(r1.getRating(), r2.getRating());
		}
	}
	
	public static class CompareByRelevanceDESC implements Comparator<Reference>{
		public int compare(Reference r1, Reference r2) {
			if (r1 == r2)
				return 0;
			
			return Float.compare(r2.getRating(), r1.getRating());
		}
	}

}
